/****h* scdbxml/callbacks.c
 *  NAME
 *    callbacks.c
 *  DESCRIPTION
 *********/
#include "callbacks.h"

/*
 * opening tag with namespace
 */
/****f* callbacks.c/callBackElementNSStart
 *  NAME
 *    callBackElementNSStart
 *  SYNOPSIS
 *    void callBackElementNSStart(void *ctxt,
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackElementNSStart(void *ctxt,
														const xmlChar *localname,
														const xmlChar *prefix,
														const xmlChar *ns,
														int nb_namespaces,          
														const xmlChar **namespaces,
														int nb_attributes,
														int nb_defaulted,
														const xmlChar **attributes
														) 
	/*{{{*/
{
	 int i, nc;
	 struct _node n;

	 while (1) 
		 {
				/*
				 * check that nodes buffer won't get full
				 * double it when needed
				 */
				while (((userWriterCtxt) ctxt)->max_nodes 
							 <= ((userWriterCtxt) ctxt)->node_count + nb_attributes
							 ) 
					{
						 ((userWriterCtxt) ctxt)->max_nodes *= 2;
						 ((userWriterCtxt) ctxt)->nodes = 
							 (node) realloc((void *) ((userWriterCtxt) ctxt)->nodes, 
															sizeof(struct _node) * ((userWriterCtxt) ctxt)->max_nodes
															);
						 if (NULL == ((userWriterCtxt) ctxt)->nodes) 
							 {
									userWriterCtxtError((userWriterCtxt) ctxt, SCDBXML_MALLOC, __LINE__, __FILE__);
									break;
							 }
					}
				
				if (SCDBXML_OK != ((userWriterCtxt) ctxt)->error) 
					{
						 break;
					}
				
				/*
				 * remember node count
				 * and get current node
				 */
				nc = ((userWriterCtxt) ctxt)->node_count;
				n = ((userWriterCtxt) ctxt)->nodes[nc];
				
				n.x = ++ ((userWriterCtxt) ctxt)->magic;
				n.y = 0;
				n.depth = ++ ((userWriterCtxt) ctxt)->depth;
				n.type = XML_ELEMENT_NODE;
				
				/*
				 * namespace uri and prefix
				 */
					if (NULL == ns || NULL == prefix) {
						 n.uri_len = 0;
						 n.uri = NULL;
						 n.prefix_len = 0;
						 n.prefix = NULL;
					}
				else
					{
						 n.uri_len = strlen((char *) ns);
						 n.uri = xmlCharStrndup((char *) ns, n.uri_len);
						 n.prefix_len = strlen((char *) prefix);
						 n.prefix = xmlCharStrndup((char *) prefix, n.prefix_len);
					}
				
				/*
				 * local name of element
				 */
				n.name_len = strlen((char *) localname);
				n.name = xmlCharStrndup((char *) localname, n.name_len);
				
				/*
				 * element nodes have no value
				 */
				n.value_len = 0;
				n.value = NULL;
				
				((userWriterCtxt) ctxt)->nodes[nc] = n;
				DBG(fprintf(stderr, "element %s\n", n.name));

				/*
				 * go deeper
				 */
				++ ((userWriterCtxt) ctxt)->depth;
				
				/*
				 * add attributes
				 */
				nb_attributes *= 5;
				for (i = 0; i < nb_attributes; i += 5) 
					{
						 /*
							* get next node
							*/
						 n = ((userWriterCtxt) ctxt)->nodes[++ nc];
						 
						 n.x = ++ ((userWriterCtxt) ctxt)->magic;
						 n.y = ++ ((userWriterCtxt) ctxt)->magic;
						 n.depth = ((userWriterCtxt) ctxt)->depth;
						 n.type = XML_ATTRIBUTE_NODE;
						 
						 /*
							* name of attribute
							*/
						 n.name_len = strlen((char *) attributes[i]);
						 n.name = xmlCharStrndup((char *) attributes[i], n.name_len);
						 
						 /*
							* value of attribute
							*/
						 n.value_len = (int)(attributes[i + 4] - attributes[i + 3]);
						 n.value = xmlCharStrndup((char *) attributes[i + 3], n.value_len);
						 
						 /*
							* check for namespace
							*/
						 if (NULL == attributes[i + 1]) 
							 {
									n.uri_len = 0;
									n.uri = NULL;
									n.prefix_len = 0;
									n.prefix = NULL;
							 }
						 else 
							 {
									n.uri_len = strlen((char *) attributes[i + 2]);
									n.uri = xmlCharStrndup((char *) attributes[i + 2], n.uri_len);
									n.prefix_len = strlen((char *) attributes[i + 1]);
									n.prefix = xmlCharStrndup((char *) attributes[i + 1], n.prefix_len);
							 }
						 
						 ((userWriterCtxt) ctxt)->nodes[nc] = n;
						 DBG(fprintf(stderr, "attribute %s=\"%s\"\n", n.name, n.value));
					}

				/*
				 * come back up again
				 */
				-- ((userWriterCtxt) ctxt)->depth;
				
				/*
				 * remember new node count
				 */
				((userWriterCtxt) ctxt)->node_count = ++ nc;
				
				break;
		 }
}
/*}}}*/

/*
 * closing tag with namespace
 */
/************ callBackElementNSStart */
/****f* callbacks.c/callBackElementNSEnd
 *  NAME
 *    callBackElementNSEnd
 *  SYNOPSIS
 *    void callBackElementNSEnd(void *ctxt,
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackElementNSEnd(void *ctxt,
													const xmlChar *localname,
													const xmlChar *prefix,
													const xmlChar *URI
													) 
	/*{{{*/
{
	 int i;
	 
	 /*
		* start on most recent node
		* move back along array
		* until first unfinished element
		*/
	 for (i = ((userWriterCtxt) ctxt)->node_count - 1;
				i >= 0 && 0 != ((userWriterCtxt) ctxt)->nodes[i].y;
				-- i);
	 
	 /*
		* now on right element
		* so increment magic number for y index
		*/
	 ((userWriterCtxt) ctxt)->nodes[i].y = ++ ((userWriterCtxt) ctxt)->magic;
	 
	 /*
		* come back up
		*/
	 -- ((userWriterCtxt) ctxt)->depth;
}
/*}}}*/

/*
 * text
 */
/************ callBackElementNSEnd */
/****f* callbacks.c/callBackChars
 *  NAME
 *    callBackChars
 *  SYNOPSIS
 *    void callBackChars(void *ctxt, 
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackChars(void *ctxt, 
									 const xmlChar *chars, 
									 int len) 
	/*{{{*/
{
	 int nc; // node count
	 struct _node n; // temporary node

	 while (1) 
		 {
				/*
				 * check that nodes buffer won't get full
				 * and double it when needed
				 */
				while (((userWriterCtxt) ctxt)->max_nodes <= ((userWriterCtxt) ctxt)->node_count) 
					{
						 ((userWriterCtxt) ctxt)->max_nodes *= 2;
						 ((userWriterCtxt) ctxt)->nodes = 
							 (node) realloc((void *) ((userWriterCtxt) ctxt)->nodes, 
															sizeof(struct _node) * ((userWriterCtxt) ctxt)->max_nodes
															);
						 
						 if (NULL == ((userWriterCtxt) ctxt)->nodes) 
							 {
									userWriterCtxtError((userWriterCtxt) ctxt, SCDBXML_MALLOC, __LINE__, __FILE__);
									break;
							 }
					}
				
				if (SCDBXML_OK != ((userWriterCtxt) ctxt)->error)
					{
						 break;
					}
	 
				/*
				 * remember node count
				 * and get node into convenience variable
				 */
				nc = ((userWriterCtxt) ctxt)->node_count;
				n = ((userWriterCtxt) ctxt)->nodes[nc];
				
				n.x = ++ ((userWriterCtxt) ctxt)->magic; // inc for x
				n.y = ++ ((userWriterCtxt) ctxt)->magic; // inc for y
				n.depth = ((userWriterCtxt) ctxt)->depth + 1;
				n.type = XML_TEXT_NODE;
				
				/*
				 * no namespace
				 */
				n.uri_len = 0;
				n.uri = NULL;
				n.prefix_len = 0;
				n.prefix = NULL;
				
				/*
				 * no name
				 */
				n.name_len = 0;
				n.name = NULL;
				
				/*
				 * value of text
				 */
				n.value_len = len;
				n.value = xmlCharStrndup((char *) chars, len);
				
				/*
				 * put back node
				 * and remember new node count
				 */
				((userWriterCtxt) ctxt)->nodes[nc] = n;
				((userWriterCtxt) ctxt)->node_count = ++ nc;
				
				//DBG(fprintf(stderr, "text %d bytes\n", len));
				break;
		 }
}
/*}}}*/

/*
 * stuff to do at start of document
 */
/************ callBackChars */
/****f* callbacks.c/callBackDocumentStart
 *  NAME
 *    callBackDocumentStart
 *  SYNOPSIS
 *    void callBackDocumentStart(void *ctxt) 
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackDocumentStart(void *ctxt) 
	/*{{{*/
{	 
	 if (SCDBXML_OK == ((userWriterCtxt) ctxt)->error) 
		 {
				/*
				 * initialise numbers
				 */
				((userWriterCtxt) ctxt)->magic = 0;
				((userWriterCtxt) ctxt)->depth = 0;
				
				/*
				 * insert name of document
				 * and get id of insert
				 */
				userWriterCtxtInsertDoc(ctxt);
		 }
}
/*}}}*/

/*
 * stuff to do at end of document
 */
/************ callBackDocumentStart */
/****f* callbacks.c/callBackDocumentEnd
 *  NAME
 *    callBackDocumentEnd
 *  SYNOPSIS
 *    void callBackDocumentEnd(void *ctxt) 
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackDocumentEnd(void *ctxt) 
	/*{{{*/
{
	 if (SCDBXML_OK == ((userWriterCtxt) ctxt)->error) 
		 {
				/*
				 * insert nodes from document
				 */
				userWriterCtxtInsertNodes(ctxt);
		 }
}
/*}}}*/

/*
 * processing instruction has been parsed
 */
/************ callBackDocumentEnd */
/****f* callbacks.c/callBackPI
 *  NAME
 *    callBackPI
 *  SYNOPSIS
 *    void callBackPI(void *ctxt,
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackPI(void *ctxt,
								const xmlChar *target,
								const xmlChar *data)
	/*{{{*/
{
	 int nc; // node count
	 struct _node n; // temporary node

	 while (1) 
		 {
				/*
				 * check that nodes buffer won't get full
				 * and double it when needed
				 */
				while (((userWriterCtxt) ctxt)->max_nodes <= ((userWriterCtxt) ctxt)->node_count) 
					{
						 ((userWriterCtxt) ctxt)->max_nodes *= 2;
						 ((userWriterCtxt) ctxt)->nodes = 
							 (node) realloc((void *) ((userWriterCtxt) ctxt)->nodes, 
															sizeof(struct _node) * ((userWriterCtxt) ctxt)->max_nodes
															);
						 if (NULL == ((userWriterCtxt) ctxt)->nodes) 
							 {
									userWriterCtxtError((userWriterCtxt) ctxt, SCDBXML_MALLOC, __LINE__, __FILE__);
									break;
							 }
					}
				if (SCDBXML_OK != ((userWriterCtxt) ctxt)->error) 
					{
						 break;
					}

				/*
				 * remember node count
				 * and get node into convenience variable
				 */
				nc = ((userWriterCtxt) ctxt)->node_count;
				n = ((userWriterCtxt) ctxt)->nodes[nc];
				
				n.x = ++ ((userWriterCtxt) ctxt)->magic; // inc for x
				n.y = ++ ((userWriterCtxt) ctxt)->magic; // inc for y
				n.depth = ((userWriterCtxt) ctxt)->depth + 1; // PIs are outside of tree depth
				n.type = XML_PI_NODE;
				
				/*
				 * no namespace
				 */
				n.uri_len = 0;
				n.uri = NULL;
				n.prefix_len = 0;
				n.prefix = NULL;
				
				/*
				 * target of PI is name
				 */
				n.name_len = strlen((char *) target);
				n.name = xmlCharStrndup((char *) target, n.name_len);
				
				/*
				 * data of PI is value
				 */
				n.value_len = strlen((char *) data);
				n.value = xmlCharStrndup((char *) data, n.value_len);
				
				/*
				 * put back node
				 * and remember new node count
				 */
				((userWriterCtxt) ctxt)->nodes[nc] = n;
				((userWriterCtxt) ctxt)->node_count = ++ nc;
				
				DBG(fprintf(stderr, "PI %s\n", n.name));
				
				break;
		 }
}
/*}}}*/

/*
 * comments
 */
/************ callBackPI */
/****f* callbacks.c/callBackCom
 *  NAME
 *    callBackCom
 *  SYNOPSIS
 *    void callBackCom(void *ctxt, 
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackCom(void *ctxt, 
								 const xmlChar *value)
	/*{{{*/
{
	 int nc; // node count
	 struct _node n; // temporary node

	 while (1) 
		 {
				/*
				 * check that nodes buffer won't get full
				 * and double it when needed
				 */
				while (((userWriterCtxt) ctxt)->max_nodes <= ((userWriterCtxt) ctxt)->node_count) 
					{
						 ((userWriterCtxt) ctxt)->max_nodes *= 2;
						 ((userWriterCtxt) ctxt)->nodes = 
							 (node) realloc((void *) ((userWriterCtxt) ctxt)->nodes, 
															sizeof(struct _node) * ((userWriterCtxt) ctxt)->max_nodes
															);
						 if (NULL == ((userWriterCtxt) ctxt)->nodes) 
							 {
									userWriterCtxtError((userWriterCtxt) ctxt, SCDBXML_MALLOC, __LINE__, __FILE__);
									break;
							 }
					}
				if (SCDBXML_OK != ((userWriterCtxt) ctxt)->error) 
					{
						 break;
					}
				
				/*
				 * remember node count
				 * and get node into convenience variable
				 */
				nc = ((userWriterCtxt) ctxt)->node_count;
				n = ((userWriterCtxt) ctxt)->nodes[nc];
				
				n.x = ++ ((userWriterCtxt) ctxt)->magic; // inc for x
				n.y = ++ ((userWriterCtxt) ctxt)->magic; // inc for y
				n.depth = ((userWriterCtxt) ctxt)->depth + 1;
				n.type = XML_COMMENT_NODE;
				
				/*
				 * no namespace
				 */
				n.uri_len = 0;
				n.uri = NULL;
				n.prefix_len = 0;
				n.prefix = NULL;
				
				/*
				 * no name
				 */
				n.name_len = 0;
				n.name = NULL;
				
				/*
				 * value of comment
				 */
				n.value_len = strlen((char *) value);
				n.value = xmlCharStrndup((char *) value, n.value_len);
				
				/*
				 * put back node
				 * and remember new node count
				 */
				((userWriterCtxt) ctxt)->nodes[nc] = n;
				((userWriterCtxt) ctxt)->node_count = ++ nc;
				
				DBG(fprintf(stderr, "Comment %s\n", n.value));
				
				break;
		 }
}
/*}}}*/

/*
 * entities
 */
/************ callBackCom */
/****f* callbacks.c/callBackEntity
 *  NAME
 *    callBackEntity
 *  SYNOPSIS
 *    void callBackEntity(void *ctxt,
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackEntity(void *ctxt,
										const xmlChar *name,
										int type,
										const xmlChar *publicID,
										const xmlChar *systemID,
										xmlChar *content) 
	/*{{{*/
{
	 xmlEntityPtr e = NULL;
	 
	 while (1) 
		 {
				/*
				 * new entity becomes start of list
				 */
				e = xmlNewEntity(NULL,
												 name,
												 type,
												 publicID,
												 systemID,
												 content);
				
				if (NULL == e) 
					{
						 userWriterCtxtError((userWriterCtxt) ctxt, SCDBXML_MALLOC, __LINE__, __FILE__);
						 break;
					}
				
				/*
				 * existing list gets added onto new start of list
				 */
				e->nexte = ((userWriterCtxt) ctxt)->entities;
				((userWriterCtxt) ctxt)->entities = e;
				
				DBG(fprintf(stderr, "New entity %s (%s)\n", ((userWriterCtxt) ctxt)->entities->name, ((userWriterCtxt) ctxt)->entities->content));
				
				break;
		 }
}

/*}}}*/

/************ callBackEntity */
/****f* callbacks.c/callBackGetEntity
 *  NAME
 *    callBackGetEntity
 *  SYNOPSIS
 *    xmlEntityPtr callBackGetEntity(void *ctxt,
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
xmlEntityPtr callBackGetEntity(void *ctxt,
															 const xmlChar *name)
	/*{{{*/
{
	 xmlEntityPtr e = NULL;
	 
	 while (1) 
		 {
				for (e = ((userWriterCtxt) ctxt)->entities;
						 NULL != e;
						 e = e->nexte) 
					{
						 if (0 == xmlStrcmp(name, e->name)) 
							 {
									break;
							 }
					}
				
				break;
		 }
	 
	 if (NULL != e) 
		 {
				DBG(fprintf(stderr, "GetEntity %s\n", name));
		 }
	 
	 return e;
}
/*}}}*/

/************ callBackGetEntity */
/****f* callbacks.c/callBackError
 *  NAME
 *    callBackError
 *  SYNOPSIS
 *    void callBackError(void *ctxt,
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void callBackError(void *ctxt,
									 const char *message,
									 ...)
	/*{{{*/
{
	 userWriterCtxtError((userWriterCtxt) ctxt, SCDBXML_XML, __LINE__, __FILE__);
}
/*}}}*/
/************ callBackError */
